/*
* This file is a part of the open-eBackup project.
* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
* If a copy of the MPL was not distributed with this file, You can obtain one at
* http://mozilla.org/MPL/2.0/.
*
* Copyright (c) [2024] Huawei Technologies Co.,Ltd.
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
*/
#include "PluginUtilities.h"
#include <boost/uuid/uuid.hpp>            // uuid class
#include <boost/uuid/uuid_generators.hpp> // generators
#include <boost/uuid/uuid_io.hpp>         // streaming operators etc.
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/xpressive/xpressive_dynamic.hpp>
#include <boost/archive/iterators/base64_from_binary.hpp>
#include <boost/archive/iterators/binary_from_base64.hpp>
#include <boost/archive/iterators/transform_width.hpp>
#include <filesystem>
#ifndef WIN32
#include <sys/ioctl.h>
#include <linux/fs.h>
#endif
#include "system/System.hpp"
#include "common/CTime.h"
#include "common/Path.h"
#include "common/EnvVarManager.h"
#include "PluginConstants.h"
#include "config_reader/ConfigIniReaderImpl.h"
#include "device_access/k8s/K8sutil.h"
#include "config_reader/ConfigIniReader.h"
#include "common/CommonStruct.h"

#ifdef WIN32
#include "FileSystemUtil.h"
#endif

using namespace std;
using namespace OBSPlugin;
using namespace Module;

namespace {
constexpr int TIME_STR_LEN = 80;
constexpr auto MODULE = "Utilities";

constexpr uint32_t NUMBER0 = 0;
constexpr uint32_t NUMBER1 = 1;
constexpr uint32_t NUMBER2 = 2;
constexpr uint32_t NUMBER3 = 3;
constexpr uint32_t NUMBER4 = 4;
constexpr uint32_t NUMBER6 = 6;
constexpr uint32_t NUMBER7 = 7;
constexpr uint32_t NUMBER8 = 8;
constexpr uint32_t NUMBER64 = 64;
constexpr uint32_t NUMBER256 = 256;
constexpr uint32_t NUMBER1024 = 1024;
constexpr uint16_t MP_FAILED = Module::FAILED;
constexpr uint16_t MP_SUCCESS = Module::SUCCESS;
const std::string DOUBLE_BACKSLASH = "\\\\";
const std::string DOUBLE_SLASH = "//";
const std::string SLASH = "/";
const std::string BACKSLASH = "\\";
const std::wstring UNC_PATH_PREFIX = LR"(\\?\)";
const std::string PATH_PREFIX = R"(\\?\)";
const std::string SNAPSHOT_DIRNAME = ".snapshot";

const std::string DIRECACHE_FILE_NAME = "dircache";
const std::string FILECACHE_FILE_NAME_0 = "filecache_0";
const std::string META_FILE_NAME_0 = "meta_file_0";
const std::string XMETA_FILE_NAME_0 = "xmeta_file_0";
const std::string META_FILE_COUNT_NAME = "metafile_count.txt";
const std::string CFGFILE = "/opt/DataBackup/ProtectClient/ProtectClient-E/conf/testcfg.tmp";
}

namespace OBSPlugin {
// 输入是ip列表，以逗号分割。检查通过管理网络访问8088端口是否连通
bool CheckDeviceNetworkConnect(const std::string &managerIps)
{
    vector<string> output;
    vector<string> errOutput;
    vector<string> paramList;
    paramList.push_back(Module::CPath::GetInstance().GetRootPath());
    paramList.push_back(managerIps);
#ifdef _AIX
    string cmd = "?/bin/checkIp.sh '?'";
#else
    string cmd = "sudo ?/bin/checkIp.sh '?'";
#endif
#ifndef WIN32
    int ret = Module::runShellCmdWithOutput(INFO, MODULE, 0, cmd, paramList, output, errOutput);
#else
    int ret = 1;
    ERRLOG("WIN32 NOT IMPLMENTED");
#endif
    if (ret != MP_SUCCESS) {
        string msg;
        for (auto &it : output) {
            msg += it + " ";
        }
        string errmsg;
        for (const auto &it : errOutput) {
            errmsg += it + " ";
        }
        HCP_Log(ERR, MODULE) << "Failed to visit all manager network, run shell ret:" << ret
                             << "run shell errmsg: " << WIPE_SENSITIVE(errmsg) << HCPENDLOG;
        return false;
    }
    return true;
}

std::string FormatTimeToStrBySetting(time_t timeInSeconds, const std::string& timeFormat)
{
#ifndef WIN32
    struct tm *tmp = localtime(&timeInSeconds);
    char time[TIME_STR_LEN];
    strftime(time, sizeof(time), timeFormat.c_str(), tmp);
    string timeStr(time);
    return timeStr;
#else
    return "";
#endif
}

std::string FormatTimeToStr(time_t timeInSeconds)
{
    return FormatTimeToStrBySetting(timeInSeconds, "%Y-%m-%d-%H:%M:%S");
}

void LogCmdExecuteError(int retCode, const std::vector<std::string> &output, const std::vector<std::string> &errOutput)
{
    string stdoutMsg;
    for (auto &it : output) {
        stdoutMsg += it + " ";
    }
    string stderrMsg;
    for (const auto &it : errOutput) {
        stderrMsg += it + " ";
    }
    HCP_Log(DEBUG, MODULE) << "run shell failed with code:" << retCode
        << ", stdout: " << WIPE_SENSITIVE(stdoutMsg)
        << ", stderr: " << WIPE_SENSITIVE(stderrMsg) << HCPENDLOG;
}

int RunShellCmd(const std::string& cmd, const vector<string>& paramList)
{
    vector<string> output;
    vector<string> erroutput;
    if (Module::runShellCmdWithOutput(INFO, MODULE, 0, cmd, paramList, output, erroutput) != MP_SUCCESS) {
        string msg;
        string errmsg;
        for (auto &it : output) {
            msg += it + " ";
        }
        ERRLOG("Run shell msg: %s", Module::WipeSensitiveDataForLog(msg).c_str());
        for (auto &it : erroutput) {
            errmsg += it + " ";
        }
        ERRLOG("Run shell error msg: %s", Module::WipeSensitiveDataForLog(errmsg).c_str());
        return MP_FAILED;
    }
    return MP_SUCCESS;
}

int RunShellCmd(const std::string& cmd, const vector<string>& paramList, std::vector<std::string>& output)
{
    vector<string> erroutput;
    if (Module::runShellCmdWithOutput(INFO, MODULE, 0, cmd, paramList, output, erroutput) != MP_SUCCESS) {
        string msg;
        for (auto &it : output) {
            msg += it + " ";
        }
        ERRLOG("kinit error msg: %s", Module::WipeSensitiveDataForLog(msg).c_str());
        return MP_FAILED;
    }
    return MP_SUCCESS;
}

size_t GenerateHash(std::string jobId)
{
    boost::hash<string> stringHash;
    return stringHash(jobId);
}

time_t GetCurrentTimeInSeconds()
{
    time_t currTime;
    Module::CTime::Now(currTime);
    return currTime;
}

std::string ConvertToReadableTime(time_t time)
{
#ifndef WIN32
    const uint32_t timeBufferSize = 80;
    char buffer[timeBufferSize];
    struct tm timeinfo {};
    localtime_r(&time, &timeinfo);
    strftime(buffer, sizeof(buffer), "%d%m%Y%H%M%S", &timeinfo);
    string timeStr(buffer);
    return timeStr;
#else
    return "None";
    ERRLOG("WIN32 NOT IMPLMENTED");
#endif
}

time_t GetCurrentTimeInSecondsString(std::string &dateAndTimeString)
{
    time_t currTime;
    Module::CTime::Now(currTime);
    dateAndTimeString = ConvertToReadableTime(currTime);
    return currTime;
}

bool WriteFile(const std::string &filePath, std::vector<std::string>& fileContent)
{
    std::ofstream writeFd {};
    for (uint32_t retryCnt = 0; retryCnt < NUMBER3; retryCnt++) {
        try {
            writeFd.open(filePath.c_str(), std::ios::out | std::ios::trunc);
            if (writeFd.is_open()) {
                break;
            }
            writeFd.close();
            Module::SleepFor(std::chrono::seconds(NUMBER1));
        } catch (std::exception &e) {
            ERRLOG("Write %s failed, ERR: %s", filePath.c_str(), e.what());
        }
    }

    if (!writeFd.is_open()) {
        char errmsg[NUMBER256];
        strerror_r(errno, errmsg, NUMBER256);
        HCP_Log(ERR, MODULE) << "Write " << WIPE_SENSITIVE(filePath) << " failed, ERR: " << errmsg << HCPENDLOG;
        return false;
    }

    for (std::string& line : fileContent) {
        writeFd << line;
    }
    writeFd.close();
    return true;
}

bool ReadFile(const std::string &filePath, std::vector<std::string>& fileContent)
{
    std::ifstream readFd {};
    for (uint32_t retryCnt = 0; retryCnt < NUMBER3; retryCnt++) {
        try {
            readFd.open(filePath.c_str(), std::ios::in);
            if (readFd.is_open()) {
                break;
            }
            readFd.close();
            Module::SleepFor(std::chrono::seconds(NUMBER1));
        } catch (std::exception &e) {
            ERRLOG("Read %s failed, ERR: %s", filePath.c_str(), e.what());
        }
    }
    if (!readFd.is_open()) {
        char errmsg[NUMBER256];
        strerror_r(errno, errmsg, NUMBER256);
        ERRLOG("Read %s failed, ERR: %s", filePath.c_str(), errmsg);
        return false;
    }
    stringstream jsonFileContent {};
    jsonFileContent << readFd.rdbuf();
    readFd.close();

    std::string currentLine {};
    while (getline(jsonFileContent, currentLine)) {
        fileContent.emplace_back(currentLine);
    }
    return true;
}

bool CreateDirectory(const std::string& path)
{
    if (path.empty()) {
        return true;
    }

    if (IsDirExist(path)) {
        DBGLOG("CreateDirectory success, dir exist: %s", path.c_str());
        return true;
    }

    string parentDir = GetPathName(path);
    if (!CreateDirectory(parentDir)) {
        ERRLOG("CreateDirectory fail for: %s", path.c_str());
        return false;
    }

#ifndef WIN32
    int res = mkdir(path.c_str(), S_IRUSR | S_IWUSR | S_IXUSR);
    if (res != Module::SUCCESS && errno != EEXIST) {
        ERRLOG("CreateDirectory fail for: %s, errno: %u", path.c_str(), errno);
        return false;
    }
    return true;
#else
    std::string dirPath = ReverseSlash(path);
    INFOLOG("call create_directory, %s", dirPath.c_str());
    std::wstring wDirPath = Module::FileSystemUtil::Utf8ToUtf16(dirPath);
    if (wDirPath.find(UNC_PATH_PREFIX) != 0) { // check UNC prefix
        wDirPath = UNC_PATH_PREFIX + wDirPath;
    }
    if (!::CreateDirectoryW(wDirPath.c_str(), nullptr)) {
        DWORD errorCode = ::GetLastError();
        if (errorCode == ERROR_ALREADY_EXISTS) {
            return true;
        }
        ERRLOG("dir %s create failed, errno %d", dirPath.c_str(), errorCode);
        return false;
    }
    return true;
#endif
}

bool RecurseCreateDirectory(const std::string& path)
{
    DBGLOG("create recur dir: %s", path.c_str());
    std::vector<std::string> splitDir;
    std::stringstream iss(path);
    std::string dir;
    while (std::getline(iss, dir, '/')) {
        if (dir.empty()) {
            continue;
        }
        splitDir.push_back(dir);
    }
#ifndef WIN32
    for (std::size_t i = 0; i < splitDir.size(); i++) {
        std::string path = "/";
        for (std::size_t j = 0; j <= i; j++) {
            path += splitDir[j] + "/";
        }
        int ret = mkdir(path.c_str(), S_IRUSR | S_IWUSR | S_IXUSR);
        int err = errno;
        if (ret != 0 && err != EEXIST) {
            ERRLOG("create dir failed: %s, err: %d", path.c_str(), err);
            return false;
        }
    }
#endif
    return true;
}

bool Remove(std::string path)
{
    bool flag = false;
    try {
        flag = boost::filesystem::exists(path);
    }
    catch (const boost::filesystem::filesystem_error &e) {
        HCP_Log(ERR, MODULE) << "path exists() exeption: " << WIPE_SENSITIVE(e.code().message())
            << ", path: " << path << HCPENDLOG;
        return false;
    }
    if (!flag) {
        return true;
    }

    constexpr uint16_t maxRemoveDirRetryCnt = 3;
    int retry = 0;
    do {
        retry++;
        try {
            boost::filesystem::remove_all(path);
        }
        catch (const boost::filesystem::filesystem_error &e) {
            ERRLOG("remove_all() exeption: %s, path: %s", WIPE_SENSITIVE(e.code().message()).c_str(), path.c_str());
        }
        if (!boost::filesystem::exists(path)) {
            break;
        }
        Module::SleepFor(std::chrono::seconds(retry));
    } while (retry < maxRemoveDirRetryCnt);

    if (boost::filesystem::exists(path)) {
        ERRLOG("Remove path fail for: %s", path.c_str());
        return false;
    }
    return true;
}

bool IsPathExists(const std::string &path)
{
#ifdef WIN32
    auto statResult = Module::FileSystemUtil::Stat(path);
    if (!statResult) {
        WARNLOG("win32 FileSystemUtil stat path %s failed, error: %d", path.c_str(), ::GetLastError());
        return false;
    }
    return true;
#else
    bool flag = false;
    try {
        flag = boost::filesystem::exists(path);
    }
    catch (const boost::filesystem::filesystem_error &e) {
        HCP_Log(ERR, MODULE) << "path exists() exeption: " << WIPE_SENSITIVE(e.code().message())
            << ", path: " << path << HCPENDLOG;
        return false;
    }
    if (!flag) {
        DBGLOG("path: %s doesn't exist", path.c_str());
        return false;
    }
    return true;
#endif
}

bool Rename(std::string srcPath, std::string dstPath)
{
    if (!IsPathExists(srcPath)) {
        ERRLOG("Can not rename, source path: %s doesn't exist", srcPath.c_str());
        return false;
    }

    if (IsPathExists(dstPath) && !Remove(dstPath)) {
        HCP_Log(ERR, MODULE) << "dst path: " << dstPath << " exist and remove failed" << HCPENDLOG;
        return false;
    }

    constexpr uint16_t maxRemoveDirRetryCnt = 3;
    int res = 0;
    int retry = 0;
    do {
        retry++;
        res = rename(srcPath.c_str(), dstPath.c_str());
        if (res == 0) {
            DBGLOG("Rename src path: %s to dst path: %s success", srcPath.c_str(), dstPath.c_str());
            break;
        }
        Module::SleepFor(std::chrono::seconds(retry));
    } while (retry < maxRemoveDirRetryCnt);
    if (res != 0) {
        char errmsg[NUMBER256] = "\0";
        strerror_r(errno, errmsg, NUMBER256);
        HCP_Log(ERR, MODULE) << "Rename failed, ERR: " << errmsg <<  " " << errno <<HCPENDLOG;
        ERRLOG("Rename src path: %s to dst path: %s failed,res : %d", srcPath.c_str(), dstPath.c_str(), res);
        return false;
    }
    return true;
}

std::string GetPathName(const std::string &filePath)
{
    char sep = '/';
#ifdef _WIN32
    sep = '\\';
#endif
    size_t fileoffset = filePath.rfind(sep, filePath.length());
    if (fileoffset != string::npos) {
        return (filePath.substr(0, fileoffset));
    }

    return ("");
}

std::string GetFileName(const std::string& filePath)
{
    char sep = '/';
#ifdef _WIN32
    sep = '\\';
#endif
    size_t fileoffset = filePath.rfind(sep, filePath.length());
    if (fileoffset == string::npos) {
        return filePath;
    }
    size_t fileNameLen = filePath.length() - fileoffset - 1;
    size_t fileStarPos = fileoffset + 1;
    return filePath.substr(fileStarPos, fileNameLen);
}

bool IsDirExist(const std::string& pathName)
{
    if (!IsPathExists(pathName)) {
        return false;
    }

    bool res = false;
    try {
        res = boost::filesystem::is_directory(pathName);
    }
    catch (const boost::filesystem::filesystem_error &e) {
        HCP_Log(ERR, MODULE) << "pathName is_directory() exeption: " << WIPE_SENSITIVE(e.code().message())
            << ", pathName: " << pathName << HCPENDLOG;
        return false;
    }
    return res;
}

bool IsFileExist(const std::string& fileName)
{
    if (!IsPathExists(fileName)) {
        return false;
    }

#ifndef WIN32
    bool res = false;
    try {
        res = boost::filesystem::is_regular_file(fileName);
    }
    catch (const boost::filesystem::filesystem_error &e) {
        HCP_Log(ERR, MODULE) << "fileName is_regular_file() exeption: " << WIPE_SENSITIVE(e.code().message())
            << ", fileName: " << fileName << HCPENDLOG;
        return false;
    }
#endif
    return true;
}

std::string FormatCapacity(uint64_t capacity)
{
    const uint64_t NUM_1024 = 1024;
    const uint64_t KB_size = NUM_1024;
    const uint64_t MB_size = NUM_1024 * KB_size;
    const uint64_t GB_size = NUM_1024 * MB_size;
    const uint64_t TB_size = NUM_1024 * GB_size;
    const uint64_t PB_size = NUM_1024 * TB_size;
    float formatCapacity;

    if ((capacity >= MB_size) && (capacity < GB_size)) {
        formatCapacity = static_cast<float>(capacity) / MB_size;
        return FloatToString(formatCapacity) + " MB";
    } else if ((capacity >= GB_size) && (capacity < TB_size)) {
        formatCapacity = static_cast<float>(capacity) / GB_size;
        return FloatToString(formatCapacity) + " GB";
    } else if ((capacity >= TB_size) && (capacity < PB_size)) {
        formatCapacity = static_cast<float>(capacity) / TB_size;
        return FloatToString(formatCapacity, NUMBER2) + " TB";
    } else if ((capacity >= KB_size) && (capacity < MB_size)) {
        formatCapacity = static_cast<float>(capacity) / KB_size;
        return FloatToString(formatCapacity) + " KB";
    } else if (capacity >= PB_size) {
        formatCapacity = static_cast<float>(capacity) / PB_size;
        return FloatToString(formatCapacity) + " PB";
    } else {
        return to_string(capacity) + " B";
    }
}

bool RemoveFile(const std::string& path)
{
    constexpr uint16_t maxRemoveDirRetryCnt = 3;
    bool res = false;
    int retry = 0;

    try {
        if (!boost::filesystem::is_regular_file(path)) {
            WARNLOG("skip remove inregular file %s", path.c_str());
            return true;
        }

        do {
            retry++;
            res = boost::filesystem::remove(path);
            if (res) {
                break;
            }
            Module::SleepFor(std::chrono::seconds(retry));
        } while (retry < maxRemoveDirRetryCnt);

        if (!res) {
            ERRLOG("Remove path fail for: %s", path.c_str());
            return false;
        }
    }
    catch (const boost::filesystem::filesystem_error &e) {
        ERRLOG("remove file %s caught exception: %s", path.c_str(), e.code().message());
        return false;
    }
    return true;
}

bool CopyFile(std::string srcfile, std::string dstfile)
{
    if (!IsFileExist(srcfile)) {
        HCP_Log(ERR, MODULE) << "src file: " << srcfile << " isn't a file" << HCPENDLOG;
        return false;
    }

    constexpr uint16_t maxRemoveDirRetryCnt = 3;
    bool res = false;
    int retry = 0;

    do {
        retry++;
        try {
            res = boost::filesystem::copy_file(
                srcfile, dstfile, boost::filesystem::copy_option::overwrite_if_exists);
        }
        catch (const boost::filesystem::filesystem_error &e) {
            HCP_Log(ERR, MODULE) << "copy_file() exeption: " << WIPE_SENSITIVE(e.code().message())
                << ", srcfile: " << srcfile << ", dstfile: " << dstfile << HCPENDLOG;
            return false;
        }
        if (res) {
            break;
        }
        Module::SleepFor(std::chrono::seconds(retry));
    } while (retry < maxRemoveDirRetryCnt);

    if (!res)
        HCP_Log(ERR, MODULE) << "failed to copy file: " << WIPE_SENSITIVE(srcfile) << HCPENDLOG;
    return res;
}

bool GetFileListInDirectory(std::string dir, std::vector<string>& fileList)
{
#ifdef WIN32
    try {
        if (!std::filesystem::exists(dir)) {
            ERRLOG("GetFileListInDirectory failed, dir: %s is not exist", dir.c_str());
            return false;
        }
        std::filesystem::directory_iterator endIter;
        for (std::filesystem::directory_iterator iter(dir); iter != endIter; ++iter) {
            if (std::filesystem::is_regular_file(iter->status())) {
                fileList.push_back(iter->path().string());
            }
        }
    } catch (const std::filesystem::filesystem_error &e) {
        HCP_Log(ERR, MODULE) << "catch boost file system exception : " << e.code().message() << " "
            << dir << HCPENDLOG;
        return false;
    }
#else
    try {
        if (!boost::filesystem::exists(dir)) {
            ERRLOG("GetFileListInDirectory failed, dir: %s is not exist", dir.c_str());
            return false;
        }
        boost::filesystem::directory_iterator endIter;
        for (boost::filesystem::directory_iterator iter(dir); iter != endIter; ++iter) {
            if (boost::filesystem::is_regular_file(iter->status())) {
                fileList.push_back(iter->path().string());
            }
        }
    } catch (const boost::filesystem::filesystem_error &e) {
        HCP_Log(ERR, MODULE) << "catch boost file system exception : " << e.code().message() << " "
            << dir << HCPENDLOG;
        return false;
    }
#endif
    return true;
}

bool GetDirListInDirectory(std::string dir, std::vector<string>& dirList, bool skipSnapshot)
{
    try {
#ifdef WIN32
        if (!std::filesystem::exists(dir)) {
            ERRLOG("GetDirListInDirectory failed, dir: %s is not exist", dir.c_str());
            return false;
        }
        std::filesystem::directory_iterator endIter;
        for (std::filesystem::directory_iterator iter(dir); iter != endIter; ++iter) {
            if (std::filesystem::is_directory(iter->status()) &&
                !(skipSnapshot && GetFileName(iter->path().string()) == SNAPSHOT_DIRNAME)) {
                dirList.push_back(GetFileName(iter->path().string()));
            }
        }
    } catch (const std::filesystem::filesystem_error &e) {
        HCP_Log(ERR, MODULE) << "catch boost file system exception : " << e.code().message() << " " << dir << HCPENDLOG;
        return false;
    }
#else
        if (!boost::filesystem::exists(dir)) {
            ERRLOG("GetDirListInDirectory failed, dir: %s is not exist", dir.c_str());
            return false;
        }
        boost::filesystem::directory_iterator endIter;
        for (boost::filesystem::directory_iterator iter(dir); iter != endIter; ++iter) {
            if (boost::filesystem::is_directory(iter->status()) &&
                !(skipSnapshot && GetFileName(iter->path().string()) == SNAPSHOT_DIRNAME)) {
                dirList.push_back(GetFileName(iter->path().string()));
            }
        }
    } catch (const boost::filesystem::filesystem_error &e) {
        HCP_Log(ERR, MODULE) << "catch boost file system exception : " << e.code().message() << " " << dir << HCPENDLOG;
        return false;
    }
#endif

    return true;
}

string GetParentDirName(const std::string& dir)
{
    string parentRelName;
    if (dir.empty()) {
        return parentRelName;
    }
    char sep = '/';
#ifdef _WIN32
    sep = '\\';
#endif
    string parentPath = GetPathName(dir);
    size_t fileoffset = parentPath.rfind(sep);
    parentRelName = parentPath.substr(fileoffset + 1);
    return parentRelName;
}

std::string FloatToString(const float &val, const uint8_t &precisson)
{
    stringstream sstream;
    sstream << setiosflags(ios::fixed) << setprecision(precisson) << val;
    return sstream.str();
}

void StripWhiteSpace(string& str)
{
    auto it = str.begin();
    while (it != str.end()) {
        if ((*it) == '\n' ||
            (*it) == '\t') {
            it = str.erase(it);
        } else {
            ++it;
        }
    }
}

std::string ReverseSlash(const std::string& path)
{
    string tmp = path;
#ifdef WIN32
    for (char& c : tmp) {
        if (c == '/') {
            c = '\\';
        }
    }
#endif
    return tmp;
}

std::string PathJoin(std::initializer_list<std::string> paths)
{
    std::string fullPath = "";
    if (paths.size() == 0) {
        return fullPath;
    }
    for (auto path : paths) {
        fullPath += !fullPath.empty() ? dir_sep : "";
        fullPath += path;
    }
#ifdef WIN32
    RemoveDoubleBackSlash(fullPath);
#else
    RemoveDoubleSlash(fullPath);
#endif
    return fullPath;
}

inline void RemoveDoubleSlash(std::string &str)
{
    std::size_t pos = 0;
    while ((pos = str.find(DOUBLE_SLASH)) != std::string::npos) {
        str.replace(pos, DOUBLE_SLASH.length(), SLASH);
    }
}

inline void RemoveDoubleBackSlash(std::string &str)
{
    std::size_t beginIndex = 0;
    if (str.find(PATH_PREFIX) == 0) {
        beginIndex = PATH_PREFIX.size();
    }
    std::size_t pos = 0;
    while ((pos = str.find(DOUBLE_BACKSLASH, beginIndex)) != std::string::npos) {
        str.replace(pos, DOUBLE_BACKSLASH.length(), BACKSLASH);
    }
}

std::string LowerCase(const std::string& path)
{
    std::string lowerPath = path;
    std::transform(lowerPath.begin(), lowerPath.end(), lowerPath.begin(),
        [](unsigned char c) { return ::tolower(c); });
    return lowerPath;
}

std::string FormatSpeed(uint64_t speed)
{
    const uint64_t KB_size = NUMBER1024;
    const uint64_t MB_size = NUMBER1024 * KB_size;
    const uint64_t GB_size = NUMBER1024 * MB_size;
    const uint64_t TB_size = NUMBER1024 * GB_size;
    const uint64_t PB_size = NUMBER1024 * TB_size;
    float formatSpeed;

    if ((speed >= MB_size) && (speed < GB_size)) {
        formatSpeed = static_cast<float>(speed) / MB_size;
        return FloatToString(formatSpeed) + "MBps";
    } else if ((speed >= GB_size) && (speed < TB_size)) {
        formatSpeed = static_cast<float>(speed) / GB_size;
        return FloatToString(formatSpeed) + "GBps";
    } else if ((speed >= TB_size) && (speed < PB_size)) {
        formatSpeed = static_cast<float>(speed) / TB_size;
        return FloatToString(formatSpeed) + "TBps";
    } else if ((speed >= KB_size) && (speed < MB_size)) {
        formatSpeed = static_cast<float>(speed) / KB_size;
        return FloatToString(formatSpeed) + "KBps";
    } else if (speed >= PB_size) {
        formatSpeed = static_cast<float>(speed) / PB_size;
        return FloatToString(formatSpeed) + "PBps";
    } else {
        return std::to_string(speed) + "Bytes/s";
    }
}

std::string GetDeployType()
{
    std::string cmd =
        RDADMIN_BASH + "\"" + Module::EnvVarManager::GetInstance()->GetAgentHomePath() + XML_READ_TYPE + "\"";
    vector<string> output;
    vector<string> errOutput;
    HCP_Log(INFO, MODULE) << "run cmd : " << cmd << HCPENDLOG;
    Module::runShellCmdWithOutput(INFO, MODULE, 0, cmd, { }, output, errOutput);
    return output[0];
}

#ifndef WIN32
string GetRealPath(const string& path)
{
    string realPath;
    char normalizePath[PATH_MAX + 1] = { 0x00 };
    if (realpath(path.c_str(), normalizePath) != nullptr) {
        realPath = normalizePath;
    }
    return realPath;
}
#endif

#ifndef WIN32
string GetDirName(const string& path)
{
    vector<char> copy(path.c_str(), path.c_str() + path.size() + 1);
    return dirname(copy.data());
}
#endif

#ifndef WIN32
bool IsDir(const string& path)
{
    struct stat st {};
    return (lstat(path.c_str(), &st) == 0) && S_ISDIR(st.st_mode);
}
#endif

std::string PrepareUnZipCommand(const std::string &workDir, const std::string &dcache,
    const std::string &fcache, const std::string &meta, const std::string &xmeta)
{
    std::string moveToDirCmd = "cd " + workDir + ";";
    std::string zipDirCacheFilesCmd = "tar -I pigz -xf " + dcache + " -C . &&";
    std::string zipFCacheFilesCmd = "tar -I pigz -xf " + fcache + " -C . &&";
    std::string zipMetaFilesCmd = "tar -I pigz -xf " + meta + " -C . &&";
    std::string zipXMetaFilesCmd = "tar -I pigz -xf " + xmeta + " -C . ";
    std::string execCmd = moveToDirCmd + zipDirCacheFilesCmd + zipFCacheFilesCmd + zipMetaFilesCmd + zipXMetaFilesCmd;
    return execCmd;
}

bool CheckUnzipMetafileSucceed(const std::string& execCmd, const std::string& workDir)
{
    vector<std::string> output;
    vector<std::string> errOutput;
    int retryCnt = 0;
    do {
        int ret = Module::runShellCmdWithOutput(INFO, MODULE, 0, execCmd, {}, output, errOutput);
        if (ret != 0) {
            ERRLOG("unzip failed, ret: %d, retry: %d", ret, retryCnt);
            for (auto msg : errOutput) {
                ERRLOG("errmsg: %s", msg.c_str());
            }
            errOutput.clear();
            Module::SleepFor(std::chrono::seconds(1));  // 暂停1秒
            continue;
        }

        // shell命令执行未报错但是实际上没有解压过去
        if (!CheckMetafileExists(workDir)) {
            ERRLOG("unzip failed though ret code is false, retry: %d", retryCnt);
            Module::SleepFor(std::chrono::seconds(1));  // 暂停1秒
            continue;
        }
        return true;
    } while (++retryCnt <= NORMAL_RETRY_TIMES);
    return false;
}

bool CheckMetafileExists(const std::string& workDir)
{
    if (IsFileExist(StandardPath(workDir + dir_sep + DIRECACHE_FILE_NAME)) &&
        IsFileExist(StandardPath(workDir + dir_sep + FILECACHE_FILE_NAME_0)) &&
        IsFileExist(StandardPath(workDir + dir_sep + META_FILE_NAME_0)) &&
        IsFileExist(StandardPath(workDir + dir_sep + XMETA_FILE_NAME_0)) &&
        IsFileExist(StandardPath(workDir + dir_sep + META_FILE_COUNT_NAME))) {
        return true;
    }
    ERRLOG("unzip metafile failed, work dir: %s", workDir.c_str());
    return false;
}

std::string StandardPath(const std::string& path)
{
    std::string temp = path;
    for (auto it = temp.begin(); it + 1 != temp.end();) {
        if (*it == Module::PATH_SEPARATOR[0] && *(it + 1) == Module::PATH_SEPARATOR[0]) {
            temp.erase(it + 1);
        } else {
            ++it;
        }
    }
    return temp;
}

void CheckAndDeleteFiles(const std::string& fileName)
{
    HCP_Log(INFO, MODULE) << "check if metafile exists: " << fileName << HCPENDLOG;
    if (IsFileExist(fileName)) {
        HCP_Log(INFO, MODULE) << "metafile exists, remove" << HCPENDLOG;
        RemoveFile(fileName);
    }
}

bool GetInnerPodIp(std::string& podIp)
{
    std::vector<std::string> fileContent;
    if (!ReadFile(CFGFILE, fileContent)) {
        HCP_Log(ERR, MODULE) << "Read file failed, filepath: " << CFGFILE << HCPENDLOG;
        return false;
    }
    for (std::string& str : fileContent) {
        if (str.find("POD_IP") != std::string::npos && str.length() > NUMBER7) {
            std::string tempPodIp;
            try {
                tempPodIp = str.substr(str.find_last_of("=") + 1);
            } catch (exception &e) {
                ERRLOG("split substr failed: %s", e.what());
                continue;
            } catch (...) {
                ERRLOG("split substr failed: unknown reason");
                continue;
            }
            podIp = tempPodIp;
            return true;
        }
    }
    HCP_Log(ERR, MODULE) << "Check please, no podIp in filepath: " << CFGFILE << HCPENDLOG;
    return false;
}

bool Base64Decryption(const std::string& encryptedkey, std::string &plainKey)
{
    using namespace boost::archive::iterators;
    if (encryptedkey.empty()) {
        plainKey = "";
        DBGLOG("encryptedkey is empty");
        return false;
    }

    using Base64DecodeIterator = transform_width<binary_from_base64<std::string::const_iterator>, NUMBER8, NUMBER6>;
    std::stringstream decodeResult;
    try {
        copy(Base64DecodeIterator(encryptedkey.begin()), Base64DecodeIterator(encryptedkey.end()),
            std::ostream_iterator<char>(decodeResult));
    } catch (...) {
        ERRLOG("Exception occured while decoding");
        plainKey = "";
        return false;
    }

    plainKey = decodeResult.str();
    return true;
}
std::string Base64Decode(const std::string &in)
{
    string out;
    bool ret = Base64Decryption(in, out);
    if (!ret) {
        out = in;
    }
    return out;
}

bool OperateIpsRule(const std::vector<std::string>& ipList, const Module::IpRuleOperation& operation)
{
    // 校验是否是内置插件，如果不是内置则直接退出
    auto configReader = Module::ConfigReaderImpl::instance();
    std::string backupScene = configReader->GetBackupSceneFromXml("backup_scene");
    // 外置代理，不设置路由
    if (backupScene == "0" || ipList.size() == 0) {
        return true;
    }
    // 软硬解耦，不设置路由
    std::string deployType = Module::k8s::GetConfigMapItem("common-conf", "deploy_type");
    if (deployType == "d8" || deployType == "d7") {
        DBGLOG("independent does not need add ip rule");
        return true;
    }
    std::string podIp;
    if (!GetInnerPodIp(podIp)) {
        ERRLOG("get pod ip failed");
        return false;
    }
    Module::ControlDeviceInfo deviceInfo;
    deviceInfo.enableProxy = false;
    auto deviceInstance = std::make_unique<Module::DoradoBlock>(deviceInfo, false);
    for (const std::string& ip : ipList) {
        if (ip.size() < NUMBER7) {
            WARNLOG("%s is  not a effctive ip", ip.c_str());
            continue;
        }
        int errcode;
        DBGLOG("enter add or delete ip rule for ip : %s", ip.c_str());
        if (!deviceInstance->OperateIpRule(ip, podIp, operation, errcode)) {
            ERRLOG("add or delete ip route for ip : %s failed, errno : %d", ip.c_str(), errcode);
            return false;
        }
    }
    return true;
}

std::string ParseDomain2IP(const std::string &domain)
{
    if (CheckIsValidIP(domain)) {
        return domain;
    }
    std::regex patternHttp("(http)://([^/:]+)(:\\d+)?");
    std::smatch result;
    if (std::regex_match(domain, result, patternHttp)) {
        return result[NUMBER2];
    }
    const std::string domainMatch = "^(?!-)[A-Za-z0-9-]+([\\-\\.]{1}"
        "[a-z0-9]+)*\\.[A-Za-z]{2,6}$";
    const std::regex pattern(domainMatch);
    if (!regex_match(domain, pattern)) {
        WARNLOG("invalid domain format: %s", domain.c_str());
        return "";
    }
    return GetIPFromDomain(domain);
}

std::string GetIPFromDomain(const std::string &domain)
{
    std::string resultIP = "";
    try {
        boost::asio::io_service io_service;
        boost::asio::ip::tcp::resolver resolver(io_service);
        boost::asio::ip::tcp::resolver::query query(domain, "");
        boost::asio::ip::tcp::resolver::iterator iter = resolver.resolve(query);
        boost::asio::ip::tcp::resolver::iterator end;
        if (iter != end) {
            boost::asio::ip::tcp::endpoint ep = *iter++;
            resultIP = ep.address().to_string();
            INFOLOG("Get ip from domain,resultIP:%s", resultIP.c_str());
            return resultIP;
        }
    } catch(std::exception& e) {
        ERRLOG("Get domain from ip failed, domain: %s, ERR: %s", domain.c_str(), e.what());
    }
    return std::string("");
}

bool CheckIsValidIPv6(const std::string& ip)
{
    // 检查是否为IPv6
    std::string ipv6RegStr = "((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|"
    "(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|"
    "(([0-9A-Fa-f]{1,4}:){5}(:[0-9A-Fa-f]{1,4}){1,2})|"
    "(([0-9A-Fa-f]{1,4}:){4}(:[0-9A-Fa-f]{1,4}){1,3})|"
    "(([0-9A-Fa-f]{1,4}:){3}(:[0-9A-Fa-f]{1,4}){1,4})|"
    "(([0-9A-Fa-f]{1,4}:){2}(:[0-9A-Fa-f]{1,4}){1,5})|"
    "(([0-9A-Fa-f]{1,4})?:(:[0-9A-Fa-f]{1,4}){1,6})|"
    "(([0-9A-Fa-f]{1,4}:){1,6}:)|(::)|(([0-9A-Fa-f]{1,4}:){6}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)"
    "(\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)){3})|"
    "(([0-9A-Fa-f]{1,4}:){5}:(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)"
    "(\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)){3})|"
    "(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,1}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)"
    "(\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)){3})|"
    "(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,2}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)"
    "(\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)){3})|"
    "(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)"
    "(\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)){3})|(([0-9A-Fa-f]{1,4})?::([0-9A-Fa-f]{1,4}:){0,4}"
    "(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)(\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)){3}))";
    static boost::xpressive::sregex reg_ipv6 =
        boost::xpressive::sregex::compile("^" + ipv6RegStr + "$"); // 匹配不带[]格式的IPV6
    if (regex_match(ip, reg_ipv6)) {
        return true;
    }
    static boost::xpressive::sregex reg_ipv61 =
    boost::xpressive::sregex::compile("^\\[" + ipv6RegStr + "\\]$"); // 匹配带[]格式的IPV6
    if (regex_match(ip, reg_ipv61)) {
        return true;
    }
    return false;
}

bool CheckIsValidIP(const std::string& ip)
{
    const std::string ipv4Match = "^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)"
        "(\\.(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)){3})$";
    boost::xpressive::sregex reg_ipv4 = boost::xpressive::sregex::compile(ipv4Match);
    if (regex_match(ip, reg_ipv4)) {
        return true;
    }
    return CheckIsValidIPv6(ip);
}

void SetPluginConfigInt(const string& keyName, int minValue, int maxValue, int defValue)
{
    Module::ConfigReader::setIntConfigInfo(OBSPlugin::DME_OBS_CONFIG_SECTION, keyName, minValue, maxValue, defValue);
}

void SetPluginConfigString(const string& keyName, const string& defaultValue)
{
    Module::ConfigReader::setStringConfigInfo(OBSPlugin::DME_OBS_CONFIG_SECTION, keyName, defaultValue);
}

bool IsPigzExist()
{
    std::string cmd = "pigz --version";
    vector<std::string> output;
    vector<std::string> errOutput;
    int ret = runShellCmdWithOutput(INFO, MODULE, 0, cmd, {}, output, errOutput);
    if (ret != 0) {
        ERRLOG("cmd failed! %d", ret);
        for (auto msg : errOutput) {
            ERRLOG("errmsg : %s", msg.c_str());
        }
    }
    return (ret == 0);
}
} // namespace
